#include "StdAfx.h"


#ifndef EXCLUDE_SCALEFORM_SDK


#include "ConfigScaleform.h"
#include "SharedStates.h"

#include "FlashPlayerInstance.h"
#include "GFileCryPak.h"
#include "GImageInfoXRender.h"
#include "GTextureXRender.h"
#include "GImage.h"
#include "GFxVideoWrapper.h"

#include <IConsole.h>
#include <StringUtils.h>
#include <CryPath.h>
#include <ILocalizationManager.h>
#include <ITexture.h>
#include <IStreamEngine.h>

#include "../CrySystem/PakVars.h"	// not really nice solution, to access enum
//////////////////////////////////////////////////////////////////////////
// CryGFxFileOpener

CryGFxFileOpener::CryGFxFileOpener()
{
}


CryGFxFileOpener& CryGFxFileOpener::GetAccess()
{
	static CryGFxFileOpener s_inst;
	return s_inst;
}


CryGFxFileOpener::~CryGFxFileOpener()
{
}


GFile* CryGFxFileOpener::OpenFile(const char* pUrl, SInt flags, SInt /*mode*/)
{
	assert(pUrl);
	if (flags & ~(GFileConstants::Open_Read|GFileConstants::Open_Buffered))
		return 0;
	const char* pExt = PathUtil::GetExt(pUrl);





#if defined(USE_GFX_VIDEO)
	if (!strcmp(pUrl, internal_video_player))
		return new GMemoryFile(pUrl, fxvideoplayer_swf, sizeof(fxvideoplayer_swf));
	if (!stricmp(pExt, "usm"))
		return new GFileCryStream(pUrl, eStreamTaskTypeVideo);
#endif
	// delegate all file I/O to ICryPak by returning a GFileCryPak object
	return new GFileCryPak(pUrl);
}


SInt64 CryGFxFileOpener::GetFileModifyTime(const char* pUrl)
{
	SInt64 fileModTime = 0;
	if (CFlashPlayer::CheckFileModTimeEnabled())
	{
		ICryPak* pPak = gEnv->pCryPak;
		if (pPak)
		{
			FILE* f = pPak->FOpen(pUrl, "rb");
			if (f)
			{
				fileModTime = (SInt64) pPak->GetModificationTime(f);
				pPak->FClose(f);
			}
		}
	}
	return fileModTime;
}


//////////////////////////////////////////////////////////////////////////
// CryGFxURLBuilder

CryGFxURLBuilder::CryGFxURLBuilder()
{
}


CryGFxURLBuilder& CryGFxURLBuilder::GetAccess()
{
	static CryGFxURLBuilder s_inst;
	return s_inst;
}


CryGFxURLBuilder::~CryGFxURLBuilder()
{
}

void CryGFxURLBuilder::BuildURL(GString* pPath, const LocationInfo& loc)
{
	assert(pPath); // GFx always passes a valid dst ptr

	const char* pOrigFilePath = loc.FileName.ToCStr();
	if (pOrigFilePath && *pOrigFilePath)
	{
		// if pOrigFilePath ends with "_locfont.[swf|gfx]", we search for the font in the localization folder
		const char locFontPostFixSwf[] = "_locfont.swf";
		const char locFontPostFixGfx[] = "_locfont.gfx";
		assert(sizeof(locFontPostFixSwf) == sizeof(locFontPostFixGfx)); // both postfixes must be of same length
		const size_t locFontPostFixLen = sizeof(locFontPostFixSwf) - 1;

		size_t filePathLength = strlen(pOrigFilePath);
		if (filePathLength > locFontPostFixLen)
		{
			size_t offset = filePathLength - locFontPostFixLen;
			if (!strnicmp(locFontPostFixGfx, pOrigFilePath + offset, locFontPostFixLen) || !strnicmp(locFontPostFixSwf, pOrigFilePath + offset, locFontPostFixLen))
			{
				*pPath = PathUtil::GetLocalizationFolder();
				*pPath += PathUtil::GetFile(pOrigFilePath);
				return;
			}
		}

		if (*pOrigFilePath != '/')
			*pPath = loc.ParentPath + loc.FileName;
		else
			*pPath = GString(&pOrigFilePath[1], filePathLength - 1);
	}
	else
	{
		assert(0 && "CryGFxURLBuilder::BuildURL() - no filename passed!");
		*pPath = "";
	}
}


//////////////////////////////////////////////////////////////////////////
// CryGFxTranslator

const size_t SharedLocStringPreallocSize = 256;

CryGFxTranslator::CryGFxTranslator()
: m_pILocMan(0)
, m_localizedString()
{
	m_localizedString.reserve(SharedLocStringPreallocSize);
}


CryGFxTranslator& CryGFxTranslator::GetAccess()
{
	static CryGFxTranslator s_inst;
	return s_inst;
}


void CryGFxTranslator::SetWordWrappingMode(const char* pLanguage)
{
	if (!stricmp(pLanguage, "japanese"))
	{
		WWMode = WWT_Prohibition;
	}
	else if (!stricmp(pLanguage, "chineset"))
	{
		WWMode = WWT_Asian;
	}
	else
	{
		WWMode = WWT_Default;
	}
}


CryGFxTranslator::~CryGFxTranslator()
{
}


UInt CryGFxTranslator::GetCaps() const
{
	return Cap_StripTrailingNewLines;
}


void CryGFxTranslator::Translate(TranslateInfo* pTranslateInfo)
{
	IF (m_pILocMan == 0, 0)
		m_pILocMan = gEnv->pSystem->GetLocalizationManager();

	if (!m_pILocMan || !pTranslateInfo)
		return;

	const wchar_t* pKey = pTranslateInfo->GetKey();

	if (!pKey || *pKey != L'@') // this is not a localizable label
		return;

	// Plain, quick and simple Unicode to UTF8 conversion of the key for Latin languages (our keys will always be in English!)
	char keyASCII[2048]; assert(wcslen(pKey) < sizeof(keyASCII));
	size_t idx(0);
	while (pKey[idx] /*&& pKey[idx] != L'\n'*/ && idx < sizeof(keyASCII) - 1)
	{
		keyASCII[idx] = (char) (pKey[idx] & 0xFF);
		++idx;
	}
	keyASCII[idx] = '\0';
	++idx;

	ITempLocStringStorage* pTmpStorage = 0;
	if (m_pILocMan->LocalizeLabel(keyASCII, m_localizedString, false, &pTmpStorage))
		pTranslateInfo->SetResult(!pTmpStorage ? m_localizedString.c_str() : pTmpStorage->GetCStr());
	assert(m_localizedString.capacity() == SharedLocStringPreallocSize);
	SAFE_RELEASE(pTmpStorage);
}


//////////////////////////////////////////////////////////////////////////
// CryGFxLog

#if defined(WIN32) || defined(WIN64)
ICVar* CryGFxLog::CV_sys_flash_debuglog(0);
int CryGFxLog::ms_sys_flash_debuglog(0);
#endif


#if defined(CRYGFXLOG_USE_IMPLICT_TLS)
#	if defined(WIN32) || defined(WIN64) || defined(XENON)
static __declspec(thread) const char* g_pCurFlashContext = 0;




#	else
#		error No TLS storage defined for CryGFxLog on this platform config
#	endif
#else
static unsigned int g_curFlashContextTlsIdx = TLS_OUT_OF_INDEXES;
#endif


CryGFxLog::CryGFxLog()
: m_pLog(0)
{
	m_pLog = gEnv->pLog;

#if defined(WIN32) || defined(WIN64)
	if (!CV_sys_flash_debuglog)
		CV_sys_flash_debuglog = REGISTER_CVAR2("sys_flash_debuglog", &ms_sys_flash_debuglog, 0,VF_NULL,"");
#endif

#if !defined(CRYGFXLOG_USE_IMPLICT_TLS)
	g_curFlashContextTlsIdx = TlsAlloc();
	assert(g_curFlashContextTlsIdx != TLS_OUT_OF_INDEXES);
#endif
}


CryGFxLog& CryGFxLog::GetAccess()
{
	static CryGFxLog s_inst;
	return s_inst;
}


CryGFxLog::~CryGFxLog()
{
#if !defined(CRYGFXLOG_USE_IMPLICT_TLS)
	if (g_curFlashContextTlsIdx != TLS_OUT_OF_INDEXES)
		TlsFree(g_curFlashContextTlsIdx);
#endif
}


void CryGFxLog::LogMessageVarg(LogMessageType messageType, const char* pFmt, va_list argList)
{
	char logBuf[1024];
	{
		const char prefix[] = "<Flash> ";

		const size_t sizeLogBuf = sizeof(logBuf);
		const size_t lenPrefix = sizeof(prefix) - 1;
		const size_t lenContex = 128;

		char* pStr = logBuf;

		// prefix
		{
			COMPILE_TIME_ASSERT(lenPrefix < sizeLogBuf);

			_snprintf(pStr, sizeLogBuf, "<Flash> ");
			pStr += lenPrefix;
		}

		// msg
		{
			COMPILE_TIME_ASSERT(lenPrefix + lenContex < sizeLogBuf);
			const size_t sizeMsg = sizeLogBuf - lenPrefix - lenContex;

			int written = _vsnprintf(pStr, sizeMsg, pFmt, argList);
			if (written < 0 || written >= sizeMsg)
			{
				pStr[sizeMsg-1] = '\0';
				written = sizeMsg - 1;
			}
			if (written && pStr[written-1] == '\n')
			{
				pStr[written-1] = '\0';
				--written;
			}
			pStr += (size_t) written;
		}

		// context
		{
#if defined(CRYGFXLOG_USE_IMPLICT_TLS)
			const char* pCurFlashContext = g_pCurFlashContext;
#else
			const char* pCurFlashContext = g_curFlashContextTlsIdx != TLS_OUT_OF_INDEXES ? (const char*) TlsGetValue(g_curFlashContextTlsIdx) : 0;
#endif
			assert(pStr - logBuf < sizeLogBuf);
			const size_t sizeCtx = sizeLogBuf - (pStr - logBuf);
			const int written = _snprintf(pStr, sizeCtx, " [%s]", pCurFlashContext ? pCurFlashContext : "#!NO_CONTEXT!#");
			if (written < 0 || (size_t)written >= sizeCtx)
				pStr[sizeCtx-1] = '\0';
		}
	}

#if defined(WIN32) || defined(WIN64)
	if (ms_sys_flash_debuglog)
		OutputDebugString(logBuf);
#endif

	switch(messageType & (~Log_Channel_Mask))
	{
	case Log_MessageType_Error:
		{
			m_pLog->LogError("%s", logBuf);
			break;
		}
	case Log_MessageType_Warning:
		{
			m_pLog->LogWarning("%s", logBuf);
			break;
		}
	case Log_MessageType_Message:
	default:
		{
			m_pLog->Log("%s", logBuf);
			break;
		}
	}
}


void CryGFxLog::SetContext(const char* pFlashContext)
{
#if defined(CRYGFXLOG_USE_IMPLICT_TLS)
	g_pCurFlashContext = pFlashContext;
#else
	if (g_curFlashContextTlsIdx != TLS_OUT_OF_INDEXES)
		TlsSetValue(g_curFlashContextTlsIdx, (void*) pFlashContext);
#endif
}


const char* CryGFxLog::GetContext() const
{
#if defined(CRYGFXLOG_USE_IMPLICT_TLS)
	return g_pCurFlashContext;
#else
	const char* pCurFlashContext = g_curFlashContextTlsIdx != TLS_OUT_OF_INDEXES ? (const char*) TlsGetValue(g_curFlashContextTlsIdx) : 0;
	return pCurFlashContext;
#endif
}


//////////////////////////////////////////////////////////////////////////
// CryGFxFSCommandHandler

CryGFxFSCommandHandler::CryGFxFSCommandHandler()
{
}


CryGFxFSCommandHandler& CryGFxFSCommandHandler::GetAccess()
{
	static CryGFxFSCommandHandler s_inst;
	return s_inst;
}


CryGFxFSCommandHandler::~CryGFxFSCommandHandler()
{
}


void CryGFxFSCommandHandler::Callback(GFxMovieView* pMovieView, const char* pCommand, const char* pArgs)
{
	// get flash player instance this action script command belongs to and delegate it to client
	CFlashPlayer* pFlashPlayer(static_cast<CFlashPlayer*>(pMovieView->GetUserData()));
	if (pFlashPlayer)
		pFlashPlayer->DelegateFSCommandCallback(pCommand, pArgs);
}


//////////////////////////////////////////////////////////////////////////
// CryGFxExternalInterface

CryGFxExternalInterface::CryGFxExternalInterface()
{
}


CryGFxExternalInterface& CryGFxExternalInterface::GetAccess()
{
	static CryGFxExternalInterface s_inst;
	return s_inst;
}


CryGFxExternalInterface::~CryGFxExternalInterface()
{
}


void CryGFxExternalInterface::Callback(GFxMovieView* pMovieView, const char* pMethodName, const GFxValue* pArgs, UInt numArgs)
{
	CFlashPlayer* pFlashPlayer(static_cast<CFlashPlayer*>(pMovieView->GetUserData()));
	if (pFlashPlayer)
		pFlashPlayer->DelegateExternalInterfaceCallback(pMethodName, pArgs, numArgs);
	else
		pMovieView->SetExternalInterfaceRetVal(GFxValue(GFxValue::VT_Undefined));
}


//////////////////////////////////////////////////////////////////////////
// CryGFxUserEventHandler

CryGFxUserEventHandler::CryGFxUserEventHandler()
{
}


CryGFxUserEventHandler& CryGFxUserEventHandler::GetAccess()
{
	static CryGFxUserEventHandler s_inst;
	return s_inst;
}


CryGFxUserEventHandler::~CryGFxUserEventHandler()
{
}


void CryGFxUserEventHandler::HandleEvent(class GFxMovieView* /*pMovieView*/, const class GFxEvent& /*event*/)
{
	// not implemented since it's not needed yet... would need to translate GFx event to independent representation
}


//////////////////////////////////////////////////////////////////////////
// CryGFxImageCreator

CryGFxImageCreator::CryGFxImageCreator()
{
}


CryGFxImageCreator& CryGFxImageCreator::GetAccess()
{
	static CryGFxImageCreator s_inst;
	return s_inst;
}


CryGFxImageCreator::~CryGFxImageCreator()
{
}


GImageInfoBase* CryGFxImageCreator::CreateImage(const GFxImageCreateInfo& info)
{
	GImageInfoBase* pImageInfo(0);
	switch (info.Type)
	{
	case GFxImageCreateInfo::Input_Image:
	case GFxImageCreateInfo::Input_File:
		{
			switch (info.Type)
			{
			case GFxImageCreateInfo::Input_Image:
				{
					// create image info and texture for internal image
					pImageInfo = new GImageInfoXRender(info.pImage);
					break;
				}
			case GFxImageCreateInfo::Input_File:
				{
					// create image info and texture for external image
					pImageInfo = new GImageInfoFileXRender(info.pFileInfo->FileName.ToCStr(), info.pFileInfo->TargetWidth, info.pFileInfo->TargetHeight);
					break;
				}
			}
			break;
		}
	default:
		{
			assert(0);
			break;
		}
	}
	return pImageInfo;
}


//////////////////////////////////////////////////////////////////////////
// CryGFxImageLoader

CryGFxImageLoader::CryGFxImageLoader()
{
}


CryGFxImageLoader& CryGFxImageLoader::GetAccess()
{
	static CryGFxImageLoader s_inst;
	return s_inst;
}


CryGFxImageLoader::~CryGFxImageLoader()
{
}


static bool LookupDDS(const char* pFilePath, uint32 &width, uint32& height)
{
	ITexture* pTex = gEnv->pRenderer->EF_GetTextureByName(pFilePath);
	if (pTex)
	{
		height = pTex->GetHeight();
		width = pTex->GetWidth();

		return true;
	}
	else
	{
		CryStackStringT<char, 256> splitFilePath(pFilePath);
		splitFilePath += ".0";

		ICryPak* pPak(gEnv->pCryPak);

		FILE* f = pPak->FOpen(splitFilePath.c_str(), "rb");
#if !defined(XENON) && !defined(PS3)
		if (!f)
			f = pPak->FOpen(pFilePath, "rb");
#endif

		if (f)
		{
			pPak->FSeek(f, 12, SEEK_SET);
			pPak->FRead(&height, 1, f);
			pPak->FRead(&width, 1, f);
			pPak->FClose(f);
		}
		return f != 0;
	}
}


GImageInfoBase* CryGFxImageLoader::LoadImage(const char* pUrl)
{
	// callback for loadMovie API
	const char* pFilePath(strstr(pUrl, "://"));
	const char* pTexId(strstr(pUrl, "://TEXID:"));
	if (!pFilePath && !pTexId)
		return 0;
	if ( pFilePath )
		pFilePath += 3;
	if ( pTexId )
		pTexId += 9;

	GImageInfoBase* pImageInfo(0);
	if (CFlashPlayer::GetFlashLoadMovieHandler())
	{
		IFlashLoadMovieImage* pImage(CFlashPlayer::GetFlashLoadMovieHandler()->LoadMovie(pFilePath));
		if (pImage && pImage->IsValid())
			pImageInfo = new GImageInfoILMISrcXRender(pImage);
	}

	if (!pImageInfo)
	{
		if ( pTexId )
		{
			int texId(atoi(pTexId));
			ITexture* pTexture(gEnv->pRenderer->EF_GetTextureByID( texId ));
			if ( pTexture )
			{
				pImageInfo = new GImageInfoTextureXRender(pTexture);
			}
		}
		else if (stricmp(pFilePath, "undefined") != 0)
		{
			const char* pExt = PathUtil::GetExt(pFilePath);
			if (!stricmp(pExt, "dds"))
			{
				// Note: We need to know the dimensions in advance as otherwise the image won't show up!
				// The seemingly more elegant approach to pass zero as w/h and explicitly call GetTexture() on the image object to 
				// query w/h won't work reliably with MT rendering (potential deadlock between Advance() and Render()). This approach 
				// works but requires that we keep in sync with any changes to the streaming system on consoles (see LookupDDS()).
				uint32 width = 0, height = 0;
				if (LookupDDS(pFilePath, width, height))
					pImageInfo = new GImageInfoFileXRender(pFilePath, width, height);
			}
#if defined(USE_GFX_JPG)
			else if(!stricmp(pExt, "jpg"))
			{
				GFxLoader2* pLoader = CSharedFlashPlayerResources::GetAccess().GetLoader(true);
				assert(pLoader);
				GPtr<GFxJpegSupportBase> pJPGLoader = pLoader->GetJpegSupport();
				if (pJPGLoader)
				{
					GFileCryPak file(pFilePath, !strnicmp(pFilePath, "%user%", 6) ? ICryPak::FOPEN_ONDISK : 0);
					GPtr<GImage> pImage = *pJPGLoader->ReadJpeg(&file, GMemory::GetGlobalHeap());
					if (pImage)
						pImageInfo = new GImageInfoXRender(pImage);
				}
			}
#endif
#if defined(USE_GFX_PNG)
			else if(!stricmp(pExt, "png"))
			{
				GFxLoader2* pLoader = CSharedFlashPlayerResources::GetAccess().GetLoader(true);
				assert(pLoader);
				GPtr<GFxPNGSupportBase> pPNGLoader = pLoader->GetPNGSupport();
				if (pPNGLoader)
				{
					GFileCryPak file(pFilePath, !strnicmp(pFilePath, "%user%", 6) ? ICryPak::FOPEN_ONDISK : 0);
					GPtr<GImage> pImage = *pPNGLoader->CreateImage(&file, GMemory::GetGlobalHeap());
					if (pImage)
						pImageInfo = new GImageInfoXRender(pImage);
				}
			}
#endif
			else if ((pExt[0] == '\0') && (pFilePath[0] == '$'))
			{
				uint32 width = 0, height = 0;
				if (LookupDDS(pFilePath, width, height))
				{
					pImageInfo = new GImageInfoFileXRender(pFilePath, width, height);
				}
			}
			else
				CryGFxLog::GetAccess().LogWarning("\"%s\" cannot be loaded by loadMovie API! Invalid file or format passed.", pFilePath);
		}
	}

	return pImageInfo;
}


#endif // #ifndef EXCLUDE_SCALEFORM_SDK
